package applet;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.Minute;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.IntervalXYDataset;

import datafeed.DataPoint;
import datafeed.LiveDataFeed;

public class DisplayApplet extends JApplet implements ActionListener {

	public static final long serialVersionUID = -7478569897994927074L;
	public static final int ITEM_PANEL_WIDTH = 300;
	public static final int ITEM_BUTTON_WIDTH = 263;
	public static final int ITEM_BUTTON_HEIGHT = 60;
	public static final int MENU_HEIGHT = 60;
	public static final int ITEM_BUTTON_PADDING = 10;
	public static final int NUMBER_OF_DATA_POINTS = 100;
	public static final int NUMBER_OF_TICKS = 8;
	
	public static ImageIcon jSpinnerButtonIcon;
	
	private CustomJMenuBar menuBar;
	private CustomJMenu categoriesMenu;
	private ButtonGroup categoriesButtonGroup;
	private List<CustomJRadioButtonMenuItem> categories;
	private CustomJMenu timeScalesMenu;
	private ButtonGroup timeScalesButtonGroup;
	private List<CustomJRadioButtonMenuItem> timeScales;
	private CustomJMenu chartTypesMenu;
	private ButtonGroup chartTypesButtonGroup;
	private List<CustomJRadioButtonMenuItem> chartTypes;
	private List<JToggleButton> m_dataItemButtons = new ArrayList<JToggleButton>();
	private List<CustomButtonPanel> buttonPanels = new ArrayList<CustomButtonPanel>();
	
	private JScrollPane itemScrollPane;
	
	private ChartPanel m_chartPanel;
	private JPanel cardPanel;
	private JPanel mainPanel;
	private Box mainPanelBox;
	private JPanel timePanel;
	private Box timePanelBox;
	private JButton timeButton;
	
	private Calendar startTime;
	private Calendar endTime;
	private String timescale;
	
	public static Font customFont;
	
	public void init() {
		
	    //Execute a job on the event-dispatching thread:
	    //creating this applet's GUI.
	    try {
	        javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
	            public void run() {
	            	createGUI();
	            }
	        });
	    } catch (Exception e) {
	        System.err.println("createGUI didn't successfully complete");
	        e.printStackTrace();
	    }
	}

	private void createGUI() {

		JOptionPane.showMessageDialog(this, getCodeBase());
		try {
			LiveDataFeed.getInstance().setCodeBase(new URL("http://localhost:8080/InteractiveDisplays"));
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		LiveDataFeed.getInstance().initialize();
		setLayout(new BorderLayout());
	    setSize(1000, 500);
	    
	    customFont = new Font("Arial", Font.PLAIN, 20);

	    endTime = Calendar.getInstance();
	    endTime.add(Calendar.HOUR, -8);
	    startTime = (Calendar)endTime.clone();
	    endTime.add(Calendar.HOUR, 8);
	    
	    //create menu
	    menuBar = new CustomJMenuBar();
	    categoriesMenu = new CustomJMenu("Category");
	    categories = new ArrayList<CustomJRadioButtonMenuItem>();
	    categoriesButtonGroup = new ButtonGroup();
	    menuBar.add(categoriesMenu);
	    List<String> categoryStrings = new ArrayList<String>();
	    LiveDataFeed.getInstance().getCategories(categoryStrings);
	    for(String s : categoryStrings) {
	    	CustomJRadioButtonMenuItem category = new CustomJRadioButtonMenuItem(s);
	    	category.setActionCommand("Category " + s);
	    	category.addActionListener(this);
	    	categoriesButtonGroup.add(category);
	    	categoriesMenu.add(category);
	    	categories.add(category);
	    }
	    categories.get(0).setSelected(true);
	    
//	    timeScalesMenu = new CustomJMenu("Time Scale");
//	    timeScales = new ArrayList<CustomJRadioButtonMenuItem>();
//	    timeScalesButtonGroup = new ButtonGroup();
//	    menuBar.add(timeScalesMenu);
//	    List<String> timeScaleStrings = new ArrayList<String>();
//	    LiveDataFeed.getInstance().getTimeScales(timeScaleStrings);
//	    for(String s : timeScaleStrings) {
//	    	CustomJRadioButtonMenuItem timeScale = new CustomJRadioButtonMenuItem(s);
//	    	timeScale.setActionCommand("Time Scale " + s);
//	    	timeScale.addActionListener(this);
//	    	timeScalesButtonGroup.add(timeScale);
//	    	timeScalesMenu.add(timeScale);
//	    	timeScales.add(timeScale);
//	    }
//	    timeScales.get(0).setSelected(true);
	    
	    chartTypesMenu = new CustomJMenu("Chart Type");
	    chartTypes = new ArrayList<CustomJRadioButtonMenuItem>();
	    chartTypesButtonGroup = new ButtonGroup();
	    menuBar.add(chartTypesMenu);
	    List<String> chartTypeStrings = new ArrayList<String>();
	    chartTypeStrings.add("Line");
	    chartTypeStrings.add("Bar");
	    for(String s : chartTypeStrings) {
	    	CustomJRadioButtonMenuItem chartType = new CustomJRadioButtonMenuItem(s);
	    	chartType.setActionCommand("Chart Type " + s);
	    	chartType.addActionListener(this);
	    	chartTypesButtonGroup.add(chartType);
	    	chartTypesMenu.add(chartType);
	    	chartTypes.add(chartType);
	    }
	    chartTypes.get(0).setSelected(true);
	    setJMenuBar(menuBar);
	    
	    //create left panel containing data items
        itemScrollPane = new JScrollPane();
        itemScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        itemScrollPane.setPreferredSize(new Dimension(300, 500));
        
        //create toggleButtons and buttonPanels
        List<String> categories = new ArrayList<String>();
        LiveDataFeed.getInstance().getCategories(categories);
        for(String s : categories) {
        	CustomButtonPanel bp = new CustomButtonPanel(s, this);
        	buttonPanels.add(bp);
        }
        
        setDataItemList(getSelectedCategory());
        

        //create cardPanel
        cardPanel = new JPanel();
        cardPanel.setLayout(new CardLayout());

        createChartPanel();
        
        //add everything to container
	    getContentPane().add(cardPanel, BorderLayout.CENTER);
	    getContentPane().add(itemScrollPane, BorderLayout.WEST);
	}
	
	private void createChartPanel() {
		
		if(mainPanel != null)
			cardPanel.remove(mainPanel);
        //create main panel
        mainPanel = new JPanel();
        mainPanelBox = Box.createVerticalBox();
        mainPanel.setLayout(new BorderLayout());
        mainPanel.add(mainPanelBox, BorderLayout.CENTER);
        
		
        //create chart
		final IntervalXYDataset dataset = createDataset();
        final JFreeChart chart = createChart(dataset);
        m_chartPanel = new ChartPanel(chart);
        m_chartPanel.setSize(new Dimension(500, 500));
        
        //create time panel
        timePanel = new JPanel();
        timePanelBox = Box.createHorizontalBox();
        timePanel.setLayout(new BorderLayout());
        timePanel.add(timePanelBox, BorderLayout.CENTER);
        timeButton = new CustomJButton("Select start and end times");
        timeButton.addActionListener(this);
        timePanelBox.add(Box.createHorizontalGlue());
        timePanelBox.add(timeButton);
        timePanelBox.add(Box.createHorizontalGlue());
        
        
        mainPanelBox.add(m_chartPanel);
        mainPanelBox.add(timePanel);
        
        cardPanel.add(mainPanel, "Main");		
	}
	
	private void setDataItemList(String category) {

		for(CustomButtonPanel bp : buttonPanels) {
			if(bp.getCategory().equals(category))
				itemScrollPane.setViewportView(bp);
		}
	}
    
    private IntervalXYDataset createDataset() {
        
        // row keys...
        List<String> dataItems = getSelectedDataItems();
        
        Map<String, List<DataPoint>> data = new HashMap<String, List<DataPoint>>();
        try {
			timescale = LiveDataFeed.getInstance().getChartData(data, startTime, endTime, NUMBER_OF_DATA_POINTS, dataItems);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 

        // create the dataset...
		TimeSeriesCollection dataset = new TimeSeriesCollection();
        
		for(String item : dataItems) {
    		TimeSeries series = new TimeSeries(item);
        	List<DataPoint> dataPoints = data.get(item);
        	for(int i = 0; i < dataPoints.size(); i++) {
        		DataPoint dp = dataPoints.get(i);
        		Calendar c = Calendar.getInstance();
        		c.setTime(dp.date);
        		if(timescale.equals("day") || timescale.equals("week") || timescale.equals("date"))
        			series.add(new Day(dp.date), dp.value);
        		else 
        			series.add(new Minute(dp.date), dp.value);
        	}
        	dataset.addSeries(series);
        }
		//dataset.setDomainIsPointsInTime(true);
        return dataset;
                
    }

    private JFreeChart createChart(final IntervalXYDataset dataset) {
        
    	String category = getSelectedCategory();
    	String timeScale = timescale;
    	String chartType = getSelectedChartType();
    	
    	JFreeChart chart = null;
    	if(chartType.equals("Line")) {
	        // create the chart...
	        chart = ChartFactory.createTimeSeriesChart(
	            category,       // chart title
	            timeScale,                    // domain axis label
	            "Value",                   // range axis label
	            dataset,                   // data
	            true,                      // include legend
	            true,                      // tooltips
	            false                      // urls
	        );
	
	        // NOW DO SOME OPTIONAL CUSTOMISATION OF THE CHART...
	//        final StandardLegend legend = (StandardLegend) chart.getLegend();
	  //      legend.setDisplaySeriesShapes(true);
	    //    legend.setShapeScaleX(1.5);
	      //  legend.setShapeScaleY(1.5);
	        //legend.setDisplaySeriesLines(true);
	
	        chart.setBackgroundPaint(Color.white);
	
	        final XYPlot plot = (XYPlot) chart.getPlot();
	        plot.setBackgroundPaint(Color.lightGray);
	        plot.setRangeGridlinePaint(Color.white);
	
	        // customise the range axis...
	        final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
	        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
	        rangeAxis.setAutoRangeIncludesZero(true);
	
	        // ****************************************************************************
	        // * JFREECHART DEVELOPER GUIDE                                               *
	        // * The JFreeChart Developer Guide, written by David Gilbert, is available   *
	        // * to purchase from Object Refinery Limited:                                *
	        // *                                                                          *
	        // * http://www.object-refinery.com/jfreechart/guide.html                     *
	        // *                                                                          *
	        // * Sales are used to provide funding for the JFreeChart project - please    * 
	        // * support us so that we can continue developing free software.             *
	        // ****************************************************************************
	        
	        // customise the renderer...
	        
	        final XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
	//        renderer.setDrawShapes(true);
	
	        renderer.setSeriesStroke(
	            0, new BasicStroke(
	                2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
	                1.0f, new float[] {10.0f, 6.0f}, 0.0f
	            )
	        );
	        renderer.setSeriesStroke(
	            1, new BasicStroke(
	                2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
	                1.0f, new float[] {6.0f, 6.0f}, 0.0f
	            )
	        );
	        renderer.setSeriesStroke(
	            2, new BasicStroke(
	                2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
	                1.0f, new float[] {2.0f, 6.0f}, 0.0f
	            )
	        );
	        // OPTIONAL CUSTOMISATION COMPLETED.
	        DateAxis axis = (DateAxis) plot.getDomainAxis();
    		if(timescale.equals("day") || timescale.equals("week") || timescale.equals("date"))
    			axis.setDateFormatOverride(new SimpleDateFormat("dd-MM-yyyy"));
    		else
    			axis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
    	}
    	else if(chartType.equals("Bar")) {
	        // create the chart...
	        chart = ChartFactory.createXYBarChart(
	            category,       // chart title
	            timeScale,                    // domain axis label
	            true,
	            "Value",                   // range axis label
	            dataset,                   // data
	            PlotOrientation.VERTICAL,  // orientation
	            true,                      // include legend
	            true,                      // tooltips
	            false                      // urls
	        );
	
	        chart.setBackgroundPaint(Color.white);
	
	        final XYPlot plot = (XYPlot) chart.getPlot();
	        plot.setBackgroundPaint(Color.lightGray);
	        plot.setRangeGridlinePaint(Color.white);
	
	        // customise the range axis...
	        final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
	        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
	        rangeAxis.setAutoRangeIncludesZero(true);

	        DateAxis axis = (DateAxis) plot.getDomainAxis();
    		if(timescale.equals("day") || timescale.equals("week") || timescale.equals("date"))
    			axis.setDateFormatOverride(new SimpleDateFormat("dd-MM-yyyy"));
    		else
    			axis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
    	}
        return chart;
    }

    private List<String> getSelectedDataItems() {
    	List<String> ret = new ArrayList<String>();
    	for(JToggleButton button : m_dataItemButtons) {
    		if(button.isSelected()) {
    			ret.add(button.getText());
    		}
    	}
    	return ret;
    }
    
    private String getSelectedCategory() {
    	return categoriesButtonGroup.getSelection().getActionCommand().replaceFirst("Category ", "");
    }
    
    private String getSelectedTimeScale() {
    	return timeScalesButtonGroup.getSelection().getActionCommand().replaceFirst("Time Scale ", "");
    }
    
    private String getSelectedChartType() {
    	return chartTypesButtonGroup.getSelection().getActionCommand().replaceFirst("Chart Type ", "");
    }
    
	@Override
	public void actionPerformed(ActionEvent arg0) {
		if(arg0.getSource() instanceof JToggleButton || arg0.getSource() instanceof JMenuItem) {

			if(arg0.getSource() instanceof JMenuItem) {
				JMenuItem menuItem = (JMenuItem) arg0.getSource();
				if(menuItem.getActionCommand().startsWith("Category ")) {
			        setDataItemList(getSelectedCategory());
				}
			}
			createChartPanel();
	        ((CardLayout)cardPanel.getLayout()).first(cardPanel);
	        
		    repaint();
		}
		else if(arg0.getSource() instanceof JButton) {
			TimeSelectionDialog dialog = 
				new TimeSelectionDialog(SwingUtilities.windowForComponent(this), "day", startTime, endTime);
			dialog.setVisible(true);
			if(dialog.getSelection() == TimeSelectionDialog.OK_OPTION) {
				startTime = dialog.getStartTime();
				endTime = dialog.getEndTime();
				createChartPanel();
		        ((CardLayout)cardPanel.getLayout()).first(cardPanel);		        
			    repaint();
			}
		}
	}
	
	private class CustomJToggleButton extends JToggleButton {
		public CustomJToggleButton(String s, boolean b) {
			super(s, b);
			setFont(customFont);
		}
		@Override
		public Dimension getPreferredSize() {
			return new Dimension(ITEM_BUTTON_WIDTH, ITEM_BUTTON_HEIGHT);
		}
		@Override
		public Dimension getMinimumSize() {
			return new Dimension(ITEM_BUTTON_WIDTH, ITEM_BUTTON_HEIGHT);
		}
		@Override
		public Dimension getMaximumSize() {
			return new Dimension(ITEM_BUTTON_WIDTH, ITEM_BUTTON_HEIGHT);
		}
	}
	
	private class CustomJButton extends JButton {
		public CustomJButton(String s) {
			super(s);
			setFont(customFont);
		}
		@Override
		public Dimension getPreferredSize() {
			int width = super.getPreferredSize().width;
			return new Dimension(width, ITEM_BUTTON_HEIGHT);
		}
		@Override
		public Dimension getMinimumSize() {
			int width = super.getMinimumSize().width;
			return new Dimension(width, ITEM_BUTTON_HEIGHT);
		}
		@Override
		public Dimension getMaximumSize() {
			int width = super.getMaximumSize().width;
			return new Dimension(width, ITEM_BUTTON_HEIGHT);
		}
	}
	
	private class CustomJMenuBar extends JMenuBar {
		@Override
		public Dimension getPreferredSize() {
			return new Dimension(100, MENU_HEIGHT);
		}
		@Override
		public Dimension getMinimumSize() {
			return new Dimension(100, MENU_HEIGHT);
		}
		@Override
		public Dimension getMaximumSize() {
			return new Dimension(2000, MENU_HEIGHT);
		}
	}
	private class CustomJMenu extends JMenu {
		
		public CustomJMenu(String s) {
			super(s);
			setFont(customFont);
		}
		@Override
		public Dimension getPreferredSize() {
			return new Dimension(super.getPreferredSize().width+20, MENU_HEIGHT);
		}
		@Override
		public Dimension getMinimumSize() {
			return new Dimension(super.getMinimumSize().width+20, MENU_HEIGHT);
		}
		@Override
		public Dimension getMaximumSize() {
			return new Dimension(super.getMaximumSize().width+20, MENU_HEIGHT);
		}
	}
	private class CustomJRadioButtonMenuItem extends JRadioButtonMenuItem {
		public CustomJRadioButtonMenuItem(String s) {
			super(s);
			setFont(customFont);
		}
		@Override
		public Dimension getPreferredSize() {
			return new Dimension(super.getPreferredSize().width+20, MENU_HEIGHT);
		}
		@Override
		public Dimension getMinimumSize() {
			return new Dimension(super.getMinimumSize().width+20, MENU_HEIGHT);
		}
		@Override
		public Dimension getMaximumSize() {
			return new Dimension(super.getMaximumSize().width+20, MENU_HEIGHT);
		}
	}

	private class CustomButtonPanel extends JPanel {

		String m_category = "";

		public CustomButtonPanel(String category, ActionListener parent) {
			super();
			m_category = category;
	        setLayout(new BorderLayout());        
	        Box vBox = Box.createVerticalBox();
	        vBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
	        List<String> items = new ArrayList<String>();
	        LiveDataFeed.getInstance().getDataItemsByCategory(items, category);
	        
	        //add buttons to panel
	        int count = 0;
	        for(String item : items) {
	        	CustomJToggleButton button = new CustomJToggleButton(item, false);
	        	button.setActionCommand("DataItem " + item);
	        	button.addActionListener(parent);
	        	m_dataItemButtons.add(button);
	        	vBox.add(button);
	        	vBox.add(Box.createVerticalStrut(10));
	        	//button.setBounds(ITEM_BUTTON_PADDING, ITEM_BUTTON_PADDING+count*(ITEM_BUTTON_HEIGHT+ITEM_BUTTON_PADDING), ITEM_BUTTON_WIDTH, ITEM_BUTTON_HEIGHT);
	        	count++;
	        }
	        add(vBox, BorderLayout.WEST);
	        setPreferredSize(new Dimension(ITEM_BUTTON_PADDING*2+ITEM_BUTTON_WIDTH, ITEM_BUTTON_PADDING+count*(ITEM_BUTTON_PADDING+ITEM_BUTTON_HEIGHT)));
		}
		
		public String getCategory() {
			return m_category;
		}
	}
	
}
